package com.linqi.freebie.http.ktorClientCio.file

import com.linqi.freebie.http.ktorClientCio.HttpClientManager
import io.ktor.client.plugins.onUpload
import io.ktor.client.request.forms.formData
import io.ktor.client.request.forms.submitFormWithBinaryData
import io.ktor.client.request.get
import io.ktor.client.statement.HttpResponse
import io.ktor.client.statement.bodyAsChannel
import io.ktor.client.statement.bodyAsText
import io.ktor.http.ContentType
import io.ktor.http.Headers
import io.ktor.http.HttpHeaders
import io.ktor.http.contentLength
import io.ktor.utils.io.ByteReadChannel
import io.ktor.utils.io.readAvailable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileOutputStream

object HttpFileHelper {

    /**
     * 上传文件，支持额外的表单字段
     * @param url 上传接口地址
     * @param file 需要上传的文件
     * @param formData 额外的表单字段
     * @return 服务器响应文本
     */
    suspend fun uploadFile(
        url: String,
        file: File,
        formData: Map<String, String> = emptyMap(),
        onProgress: (sentBytes: Long, totalBytes: Long) -> Unit
    ): String {
        val client = HttpClientManager.getClient()
        var uploadedBytes = 0L
        val totalBytes = file.length()

        val response: HttpResponse = client.submitFormWithBinaryData(
            url = url,
            formData = formData {
                formData.forEach { (k, v) -> append(k, v) }
                append("file", file.readBytes(), Headers.Companion.build {
                    append(HttpHeaders.ContentType, ContentType.Application.OctetStream.toString())
                    append(HttpHeaders.ContentDisposition, "filename=${file.name}")
                })
            }
        ) {
            onUpload { bytesSentTotal, _ ->
                uploadedBytes = bytesSentTotal
                onProgress(uploadedBytes, totalBytes)
            }
        }
        return response.bodyAsText()
    }


    /**
     * 下载文件，流式写入本地磁盘，防止大文件内存溢出
     * @param url 下载文件链接
     * @param destFile 目标本地文件路径
     * @return 是否下载成功
     */
    suspend fun downloadFile(
        url: String,
        destPath: String = "E:\\download",  // 默认下载目录
        fileName: String? = null,           // 可选自定义文件名
        onProgress: (downloadedBytes: Long, totalBytes: Long?) -> Unit
    ): Boolean {
        val client = HttpClientManager.getClient()

        // 确保目录存在
        val downloadDir = File(destPath).apply {
            if (!exists()) mkdirs()
        }

        // 自动从URL提取文件名（如果未指定）
        val outputFile = fileName?.let { File(downloadDir, it) }
            ?: File(downloadDir, url.substringAfterLast('/'))

        return try {
            val response: HttpResponse = client.get(url)
            if (response.status.value == 200) {
                val channel: ByteReadChannel = response.bodyAsChannel()
                val contentLength = response.contentLength()
                var downloaded = 0L
                val buffer = ByteArray(8192)

                withContext(Dispatchers.IO) {
                    FileOutputStream(outputFile).use { fos ->
                        while (!channel.isClosedForRead) {
                            val bytesRead = channel.readAvailable(buffer)
                            if (bytesRead == -1) break
                            fos.write(buffer, 0, bytesRead)
                            downloaded += bytesRead
                            onProgress(downloaded, contentLength)
                        }
                    }
                }
                true
            } else {
                false
            }
        } catch (e: Exception) {
            outputFile.delete() // 下载失败删除不完整文件
            false
        }
    }


    /**
     * 返回 ByteArray
     */
    suspend fun downloadFileToByteArray(
        url: String,
        onProgress: (downloadedBytes: Long, totalBytes: Long?) -> Unit
    ): ByteArray? {
        val client = HttpClientManager.getClient()

        return try {
            val response: HttpResponse = client.get(url)
            if (response.status.value == 200) {
                val channel: ByteReadChannel = response.bodyAsChannel()
                val contentLength = response.contentLength()
                var downloaded = 0L
                val buffer = ByteArray(8192)
                val outputStream = ByteArrayOutputStream()

                withContext(Dispatchers.IO) {
                    while (!channel.isClosedForRead) {
                        val bytesRead = channel.readAvailable(buffer)
                        if (bytesRead == -1) break
                        outputStream.write(buffer, 0, bytesRead)
                        downloaded += bytesRead
                        onProgress(downloaded, contentLength)
                    }
                }
                outputStream.toByteArray()
            } else {
                null
            }
        } catch (e: Exception) {
            null
        }
    }


}